package unsafe_pointer

// 参考地址： https://juejin.cn/post/7083853142403579911
// 值传递
import (
	"fmt"
	"reflect"
	"unsafe"
)

func ValueParam() {
	d := 1
	fmt.Printf("d address : %p\n", &d)
	add(&d)
	println(d) // 1
}

func add(a *int) {
	fmt.Printf("add args address : %p\n", a)
	*a = *a + 1
}

// 不能对指针的地址进行算术运算
func PointerOperation() {
	a := 1
	addr := &a
	// addr++  编译不通过
	*addr++        // 编译通过
	fmt.Println(a) // 2
}

// 两个任意指针类型不能随意转换 只有两个类型的底层数据类型是一致的，才可以完成转换
func PointerConvert() {
	type MyInt int64
	type T1 *int64
	type T2 *MyInt

	var a *int64
	var myInt *MyInt

	var t1 T1
	t1 = a // t1 是 *int64类型，a 是 *int64 类型，可以隐式转换

	var t2 T2
	t2 = myInt       // t2 是 *MyInt类型，myInt 是 *MyInt类型，可以隐式转换
	t2 = (*MyInt)(a) // t2 的底层类型是 *int64，a 是 *int64 类型，需要显式转换

	t1 = (*int64)((*MyInt)(t2)) // t2 的底层类型是 *int64，t1 是 *int64类型，需要显式转换

	fmt.Println("t1:", t1)
}

/*
Sizeof、Offsetof,Alignof 这三个函数的返回值的类型均为内置类型 uintptr，uintptr 是一个整数值，来保存变量的内存地址，可以和 Pointer 相互转换。
任意类型的指针可以转为 Pointer
Pointer 可以转为任意类型的指针
uintptr 可以转为 Pointer
Pointer 可以转为 uintptr
*/

func PointerUse() {
	a := int(1)
	fmt.Println("uinptr:", uintptr(unsafe.Pointer(&a)))

	b := (*int64)(unsafe.Pointer(&a)) // 将 *int 先转为 Pointer，再转为 *int64
	fmt.Println("typeOf:", reflect.TypeOf(b))

	c := uintptr(unsafe.Pointer(&a)) // 将 *int 先转为 Pointer，再转为 uintptr

	fmt.Printf("%p\n", b) // 打印地址 0xc0003cdbb0
	fmt.Printf("%x\n", c) // 地址 c0002124b8

	type T struct {
		a string
		b int
	}
	t := T{a: "abcdefghie", b: 3}

	/*
		1. 将 t 的地址转为 Pointer：符合第一种
		2. 将 Pointer 转为 uintptr 后得到地址的整数值：符合第四种
		3. 加上 t.b 的offset，得到 t.b 的地址整数值：uintptr是整数，可以直接相加
		4. 将 uintptr 转为 Pointer：符合第三种
		5. 将 Pointer 转为 *int :符合第二种
		6. 最后解引用，得到具体的值
	*/
	fmt.Println("uintptr(unsafe.Pointer(&t):", uintptr(unsafe.Pointer(&t)), ":", unsafe.Offsetof(t.b))
	d := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&t)) + unsafe.Offsetof(t.b)))

	str := "abcdefgiiabcdefgiiabcdefgiiabcdefgiiabcdefgiiabcdefgii"
	fmt.Println("pointer size:", unsafe.Sizeof(&d), ",str size:", unsafe.Sizeof(&str)) // 3
}

// Pointer 越过了类型检查，可以直接操作底层的内存，因此使用时需要格外小心。
// 对于 Pointer的操作，只有如下六种是合法的，其余的使用方式均为非法

// 1 利用 Pointer 作为中介，完成 T1 类型 到 T2 类型的转换
// T1 和 T2 是任意类型，如果 T1 的内存占用大于等于 T2，并且 T1 和 T2 的内存布局一致，可以利用 Pointer 作为中介，完成 T1类型 到 T2类型的转换。
// 如果T1 的内存占用小于 T2，那么 T2 剩余部分没法赋值，就会有问题

func SliceConvertToStr(s []byte) string {
	return *(*string)(unsafe.Pointer(&s))
}

func StrConvertToSlice(str string) []byte {
	return *(*[]byte)(unsafe.Pointer(&str))
}

// 使用方式二：将 Pointer 转为 uintptr (不再转回 Pointer)
// 将 Pointer 转为 uintptr，并且不再转回 Pointer，此方式用处不大，通常我们只用来打印值。
// 此方式相当于取变量的内存地址，由于 uintptr 是个变量值，而非引用，后续该变量被移动到其他位置，其对应的uintptr值不会更新；其次，如果后续没有使用该变量，随时可能会被垃圾回收掉。

func PointerConvertToUintPtr() {
	a := int(10)
	fmt.Printf("%p\n", &a)                          // 0xc0001184b8
	fmt.Printf("%x\n", uintptr(unsafe.Pointer(&a))) // c0001184b8
}
